﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.Reflection;


namespace RedSand.Web.Mvc
{
    public class RsDataManager
    {
        private static bool Debug
        {
            get
            {
                return Convert.ToBoolean(ConfigurationManager.AppSettings["DataManagerDebug"]);
            }
        }
        /// <summary>
        /// Trả về chuỗi kết nối csdl
        /// </summary>
        public static string ConnectionString
        {
            get
            {
                var value = ConfigurationManager.ConnectionStrings["RsConnectionString"];
                if (value == null) value = ConfigurationManager.ConnectionStrings[0];
                if (value == null) throw new Exception("Chuỗi kết nối csdl chưa có hoặc không đúng, nên đặt chuỗi kết nối csdl với tên là \"RsConnectionString\"");
                return value.ConnectionString;
            }
        }
        /// <summary>
        /// Xóa một bản ghi dữ liệu
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>true nếu thành công, flase nếu lỗi</returns>
        public static bool Delete(string ProcedureName, object Parameters)
        {
            return ExecuteGetReturn(ProcedureName, Parameters) > 0;
        }
        /// <summary>
        /// Xóa một bản ghi dữ liệu
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="KeyName">Tên khóa</param>
        /// <param name="KeyValue">Giá trị khóa</param>
        /// <returns></returns>
        public static bool Delete(string ProcedureName, string KeyName, object KeyValue)
        {
            Dictionary<string, object> Parameters = new Dictionary<string, object>();
            Parameters.Add(KeyName, KeyValue);
            return ExecuteGetReturn(ProcedureName, Parameters) > 0;
        }
        /// <summary>
        /// Lưu mới hoặc cập nhật dữ liệu
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Info">Đối tượng dữ liệu</param>
        /// <returns>rowcount nếu update, identity nếu insert</returns>
        public static int Save(string ProcedureName, object Info)
        {
            return ExecuteGetReturn(ProcedureName, Info, null);
        }
        /// <summary>
        /// Lưu mới hoặc cập nhật dữ liệu
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Info">Đối tượng dữ liệu</param>
        /// <param name="Parameters">Các tham số khác</param>
        /// <returns>rowcount nếu update, identity nếu insert</returns>
        public static int Save(string ProcedureName, object Info, object Parameters)
        {
            return ExecuteGetReturn(ProcedureName, Info, Parameters);
        }
        /// <summary>
        /// Lấy ID mới nhất của một bảng
        /// </summary>
        /// <param name="TableName">Tên bảng</param>
        /// <returns>ID</returns>
        public static int GetLastID(string TableName)
        {
            return ExecuteScalar<int>("Select IDENT_CURRENT('" + TableName + "') as LastID");
        }
        /// <summary>
        /// Lấy tổng số bản ghi một bảng
        /// </summary>
        /// <param name="TableName">Tên bảng</param>
        /// <returns>Số bản ghi</returns>
        public static int GetRowCount(string TableName)
        {
            return ExecuteScalar<int>("Select count(*) from " + TableName + " as RowCount");
        }
        /// <summary>
        /// Lấy một đối tượng dữ liệu
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>Đối tượng</returns>
        public static TClass GetInfo<TClass>(string ProcedureName, object Parameters) where TClass : class,new()
        {
            TClass info = default(TClass);
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            AddParameters(dbCmd, Parameters);
            try
            {
                dbConn.Open();
                SqlDataReader dr = dbCmd.ExecuteReader();
                if (dr.Read())
                {
                    info = GetInfo<TClass>(dr);
                }
                dr.Close();
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
            return info;
        }
        /// <summary>
        /// Lấy một đối tượng dữ liệu
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="KeyName">Tên khóa</param>
        /// <param name="KeyValue">Giá trị khóa</param>
        /// <returns>Đối tượng</returns>
        public static TClass GetInfo<TClass>(string ProcedureName, string KeyName, object KeyValue) where TClass : class,new()
        {
            Dictionary<string, object> Parameters = new Dictionary<string, object>();
            Parameters.Add(KeyName, KeyValue);
            return GetInfo<TClass>(ProcedureName, Parameters);
        }
        /// <summary>
        /// Lấy danh sách đối tượng dữ liệu
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <returns>Danh sách</returns>
        public static List<TClass> GetList<TClass>(string ProcedureName) where TClass : class,new()
        {
            return GetList<TClass>(ProcedureName, null);
        }
        /// <summary>
        /// Lấy danh sách đối tượng dữ liệu (phân trang)
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="PageIndex">Trang hiện tại</param>
        /// <param name="PageSize">Kích thước trang</param>
        /// <param name="TotalRecord">Tổng bản ghi (output)</param>
        /// <returns>Danh sách</returns>
        public static List<TClass> GetList<TClass>(string ProcedureName, int PageIndex, int PageSize, ref int TotalRecord) where TClass : class,new()
        {
            return GetList<TClass>(ProcedureName, null, PageIndex, PageSize, ref TotalRecord);
        }
        /// <summary>
        /// Lấy danh sách đối tượng dữ liệu
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>Danh sách</returns>
        public static List<TClass> GetList<TClass>(string ProcedureName, object Parameters) where TClass : class,new()
        {
            List<TClass> list = null;
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            AddParameters(dbCmd, Parameters);
            try
            {
                dbConn.Open();
                SqlDataReader dr = dbCmd.ExecuteReader();
                if (dr.Read())
                {
                    list = new List<TClass>();
                    do
                    {
                        list.Add(GetInfo<TClass>(dr));
                    }
                    while (dr.Read());
                }
                dr.Close();
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
            return list;
        }
        /// <summary>
        /// Lấy danh sách đối tượng dữ liệu (phân trang)
        /// </summary>
        /// <typeparam name="TClass">Tên lớp (model)</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <param name="PageIndex">Trang hiện tại</param>
        /// <param name="PageSize">Kích thước trang</param>
        /// <param name="TotalRecord">Tổng bản ghi (output)</param>
        /// <returns>Danh sách</returns>
        public static List<TClass> GetList<TClass>(string ProcedureName, object Parameters, int PageIndex, int PageSize, ref int TotalRecord) where TClass : class,new()
        {
            List<TClass> list = null;
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            AddParameters(dbCmd, Parameters);
            dbCmd.Parameters.AddWithValue("PageIndex", PageIndex);
            dbCmd.Parameters.AddWithValue("PageSize", PageSize);
            dbCmd.Parameters.AddWithValue("TotalRecord", TotalRecord).Direction = ParameterDirection.Output;
            try
            {
                dbConn.Open();
                SqlDataReader dr = dbCmd.ExecuteReader();
                if (dr.Read())
                {
                    list = new List<TClass>();
                    do
                    {
                        list.Add(GetInfo<TClass>(dr));
                    }
                    while (dr.Read());
                }
                dr.Close();
                TotalRecord = Convert.ToInt32(dbCmd.Parameters["TotalRecord"].Value);
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
            }
            finally
            {
                dbConn.Close();
            }
            return list;
        }
        /// <summary>
        /// Thực thi một câu lệnh sql
        /// </summary>
        /// <param name="Query">Lệnh</param>
        /// <returns>true nếu thành công, flase nếu lỗi</returns>
        public static bool ExecuteNonQuery(string Query)
        {
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(Query, dbConn);
            try
            {
                dbConn.Open();
                dbCmd.ExecuteNonQuery();
                return true;
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
                return false;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
        }
        /// <summary>
        /// Thực thi một thủ tục sql
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>true nếu thành công, false nếu lỗi</returns>
        public static bool ExecuteNonQuery(string ProcedureName, object Parameters)
        {
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            AddParameters(dbCmd, Parameters);

            try
            {
                dbConn.Open();
                dbCmd.ExecuteNonQuery();
                return true;
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
                return false;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
        }
        /// <summary>
        /// Thực thi một thủ tục sql, trả về giá trị của Return_Value
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <returns>Giá trị trả về (Return_Value)</returns>
        public static int ExecuteGetReturn(string ProcedureName)
        {
            return ExecuteGetReturn(ProcedureName, null, null);
        }
        /// <summary>
        /// Thực thi một thủ tục sql, trả về giá trị của Return_Value
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>Giá trị trả về (Return_Value)</returns>
        public static int ExecuteGetReturn(string ProcedureName, object Parameters)
        {
            return ExecuteGetReturn(ProcedureName, Parameters, null);
        }
        /// <summary>
        /// Thực thi một thủ tục sql, trả về giá trị của Return_Value
        /// </summary>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <param name="Parameters2">Các tham số 2</param>
        /// <returns>Giá trị trả về (Return_Value)</returns>
        public static int ExecuteGetReturn(string ProcedureName, object Parameters, object Parameters2)
        {
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            dbCmd.Parameters.AddWithValue("RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;
            AddParameters(dbCmd, Parameters);
            AddParameters(dbCmd, Parameters2);
            try
            {
                dbConn.Open();
                dbCmd.ExecuteReader();
                return (int)dbCmd.Parameters["RETURN_VALUE"].Value;
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
                return 0;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
        }
        /// <summary>
        /// Thực thi một câu lệnh sql, trả về giá trị ô đầu tiên trong bảng kết quả
        /// </summary>
        /// <typeparam name="TType">Kiểu dữ liệu</typeparam>
        /// <param name="ProcedureName">Lệnh sql</param>
        /// <returns>dữ liệu cần lấy</returns>
        public static TType ExecuteScalar<TType>(string Query)
        {
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(Query, dbConn);
            try
            {
                dbConn.Open();
                return (TType)Convert.ChangeType(dbCmd.ExecuteScalar(), typeof(TType));
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
            return default(TType);
        }
        /// <summary>
        /// Thực thi một thủ tục sql, trả về giá trị ô đầu tiên trong bảng kết quả
        /// </summary>
        /// <typeparam name="TType">Kiểu dữ liệu</typeparam>
        /// <param name="ProcedureName">Tên thủ tục</param>
        /// <param name="Parameters">Các tham số</param>
        /// <returns>dữ liệu cần lấy</returns>
        public static TType ExecuteScalar<TType>(string ProcedureName, object Parameters)
        {
            SqlConnection dbConn = new SqlConnection(ConnectionString);
            SqlCommand dbCmd = new SqlCommand(ProcedureName, dbConn);
            dbCmd.CommandType = CommandType.StoredProcedure;
            AddParameters(dbCmd, Parameters);
            try
            {
                dbConn.Open();
                return (TType)dbCmd.ExecuteScalar();
            }
            catch (Exception ex)
            {
                if (Debug) throw ex;
            }
            finally
            {
                dbConn.Close();
                dbConn.Dispose();
                dbCmd.Dispose();
            }
            return default(TType);
        }

        #region private
        /// <summary>
        /// Thêm tham số cho SqlCommand
        /// </summary>
        private static void AddParameters(SqlCommand DbCmd, object Parameters)
        {
            try
            {
                if (Parameters != null)
                {
                    if (Parameters.GetType().Name.Contains("Dictionary"))
                    {
                        foreach (var item in Parameters as Dictionary<string, object>)
                        {
                            DbCmd.Parameters.AddWithValue(item.Key, item.Value ?? DBNull.Value);
                        }
                    }
                    else
                    {
                        PropertyInfo[] properties = Parameters.GetType().GetProperties();
                        foreach (var pro in properties)
                        {
                            DbCmd.Parameters.AddWithValue(pro.Name, pro.GetValue(Parameters, null) ?? DBNull.Value);
                        }
                    }
                }
            }
            catch
            {
                throw new Exception("The parameter is not Dictionary");
            }
        }
       
        /// <summary>
        /// Lấy info từ SqlDataReader
        /// </summary>
        private static TClass GetInfo<TClass>(SqlDataReader dr) where TClass : class,new()
        {
            TClass retVal = new TClass();
            PropertyInfo[] properties = retVal.GetType().GetProperties();
            foreach (var pro in properties)
            {
                if (pro.CanWrite)
                {
                    pro.SetValue(retVal, dr[pro.Name] == DBNull.Value ? null : dr[pro.Name], null);
                }
            }
            return retVal;
        }
        #endregion
    }
}
